# 尝试导入Tkinter模块作为tk，用于构建GUI - Python 2
try:
    import Tkinter as tk
    import tkFileDialog  # 用于打开文件对话框
except ImportError:
    import tkinter as tk  # 用于构建GUI - Python 3
    import tkinter.filedialog as tkFileDialog

import json  # 用于将功能保存为JSON文件
from .reader import Reader, R420, ARU2400, FX9600  # 用于控制读取器

# 读取器类的字典，包括通用读取器和具体型号的读取器
readerClasses = {
    'Generic': Reader,
    'R420': R420,
    'ARU2400': ARU2400,
    'FX9600': FX9600
}


class InventoryApp(object):
    '''应用程序的主窗口
    '''

    def __init__(self):
        # 创建Tkinter根窗口
        self.root = tk.Tk()
        self.reader = None  # 读取器对象
        self.tags = []  # 存储读取到的标签列表
        self.trackedTags = []  # 存储跟踪的标签列表

        # 初始放置在屏幕中间
        sizeX, sizeY = 800, 550
        screenX, screenY = self.root.winfo_screenwidth(), self.root.winfo_screenheight()
        self.root.geometry('{}x{}+{}+{}'.format(sizeX, sizeY,
                                                int(screenX / 2 - sizeX / 2), int(screenY / 2 - sizeY / 2)))
        self.root.title('Tag inventory')  # 设置窗口标题

        headFont = ('Arial', 18)  # 头部字体设置

        # 创建读取器配置面板
        self.conf = tk.Frame(self.root)
        self.conf.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
        # 标题
        row = 0
        tk.Label(self.conf, text='Reader settings', font=headFont).grid(row=row, column=0, columnspan=2)
        # IP地址
        row += 1
        self.ip = tk.StringVar()
        self.ip.set('192.168.5.2')
        tk.Label(self.conf, text='IP address').grid(row=row, column=0, sticky=tk.W)
        tk.Entry(self.conf, textvariable=self.ip).grid(row=row, column=1, sticky=tk.W)
        # 读取器型号
        row += 1
        self.model = tk.StringVar()
        self.model.set('Generic')
        tk.Label(self.conf, text='Reader model').grid(row=row, column=0, sticky=tk.W)
        tk.OptionMenu(self.conf, self.model, *(readerClasses.keys())).grid(row=row, column=1, sticky=tk.W + tk.E)
        # 连接按钮
        row += 1
        self.btnConnect = tk.Button(self.conf, text='Connect reader', command=self.connect)
        self.btnConnect.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E)

        # 创建找到标签列表
        taglist = tk.Frame(self.root)
        taglist.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        # 标题
        self.tagsHeader = tk.StringVar()
        self.tagsHeader.set('Tags detected')
        tk.Label(taglist, textvariable=self.tagsHeader, font=headFont).pack()
        # 包含检测到标签的列表
        scrollbar = tk.Scrollbar(taglist, orient=tk.VERTICAL)
        self.tagsDetected = tk.Listbox(taglist, yscrollcommand=scrollbar.set, selectmode=tk.SINGLE)
        scrollbar.config(command=self.tagsDetected.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tagsDetected.pack(fill=tk.BOTH, expand=True)
        self.tagsDetected.bind('<ButtonRelease-1>', self.selectTag)
        # 用于过滤标签的按钮
        tk.Button(taglist, text='Track tags', command=self.trackTags).pack(fill=tk.X)
        tk.Button(taglist, text='Clear tracked', command=self.clearTrackedTags).pack(fill=tk.X)

        # 创建所选标签信息面板
        taginfo = tk.Frame(self.root)
        taginfo.pack(side=tk.LEFT, fill=tk.Y, expand=True, padx=5, pady=5)
        # 标题
        tk.Label(taginfo, text='Tag info', font=headFont).pack()
        # 标签信息字符串
        self.tagInfo = tk.StringVar()
        tk.Label(taginfo, textvariable=self.tagInfo, anchor=tk.W, justify=tk.LEFT).pack()

        # 开始工作
        self.root.mainloop()

    def buildSettings(self):
        row = 3

        # 发射功率
        pows = self.reader.power_table
        row += 1
        self.power = tk.DoubleVar()
        self.power.set(pows[-1])
        tk.Label(self.conf, text='Tx power [dBm]').grid(row=row, column=0, sticky=tk.W)
        tk.Scale(self.conf, from_=pows[0], to=pows[-1], resolution=pows[1] - pows[0], variable=self.power,
                 orient=tk.HORIZONTAL).grid(row=row, column=1, sticky=tk.W + tk.E)

        # 频率
        row += 1
        self.freq = tk.DoubleVar()
        self.freq.set(self.reader.freq_table[0])
        tk.Label(self.conf, text='Frequency [MHz]').grid(row=row, column=0, sticky=tk.W)
        tk.OptionMenu(self.conf, self.freq, *(self.reader.freq_table)).grid(row=row, column=1, sticky=tk.W + tk.E)

        # 预设模式
        row += 1
        modeID = self.reader.mode_table[0]
        self.presetmode = tk.IntVar()
        self.presetmode.set(modeID)
        tk.Label(self.conf, text='Preset mode').grid(row=row, column=0, sticky=tk.W)
        tk.OptionMenu(self.conf, self.presetmode, *(self.reader.mode_table), command=self.displayModeInfos).grid(
            row=row, column=1, sticky=tk.W + tk.E)

        # 预设模式信息
        row += 1
        self.modeInfo = tk.StringVar()
        self.displayModeInfos(modeID)
        tk.Label(self.conf, textvariable=self.modeInfo, anchor=tk.W, justify=tk.LEFT).grid(row=row, column=1,
                                                                                           sticky=tk.W + tk.E)

        # 搜索模式
        if isinstance(self.reader, R420):
            row += 1
            self.searchmode = tk.IntVar()
            self.searchmode.set(0)
            tk.Label(self.conf, text='Search mode').grid(row=row, column=0, sticky=tk.W)
            tk.OptionMenu(self.conf, self.searchmode, 0, 1, 2, 3).grid(row=row, column=1, sticky=tk.W + tk.E)

        # 会话
        row += 1
        self.session = tk.IntVar()
        self.session.set(1)
        tk.Label(self.conf, text='Session').grid(row=row, column=0, sticky=tk.W)
        tk.OptionMenu(self.conf, self.session, 0, 1, 2, 3).grid(row=row, column=1, sticky=tk.W + tk.E)

        # 标签种群
        row += 1
        self.population = tk.IntVar()
        self.population.set(4)
        tk.Label(self.conf, text='Tag population').grid(row=row, column=0, sticky=tk.W)
        tk.Entry(self.conf, textvariable=self.population).grid(row=row, column=1, sticky=tk.W)

        # 盘存持续时间
        row += 1
        self.duration = tk.DoubleVar()
        self.duration.set(1.0)
        tk.Label(self.conf, text='Inventory duration [s]').grid(row=row, column=0, sticky=tk.W)
        tk.Entry(self.conf, textvariable=self.duration).grid(row=row, column=1, sticky=tk.W)

        # 天线
        row += 1
        self.antennas = tk.StringVar()
        self.antennas.set('all')
        tk.Label(self.conf, text='Antennas').grid(row=row, column=0, sticky=tk.W)
        tk.Entry(self.conf, textvariable=self.antennas).grid(row=row, column=1, sticky=tk.W)

        # 单次盘存按钮
        row += 1
        tk.Button(self.conf, text='Single inventory', command=self.singleInventory).grid(row=row, column=0,
                                                                                         columnspan=2,
                                                                                         sticky=tk.W + tk.E + tk.S)

        # 实时盘存按钮
        row += 1
        self.liveStateTxt = tk.StringVar()
        self.liveStateTxt.set('Live inventory')
        tk.Button(self.conf, textvariable=self.liveStateTxt, command=self.liveInventory).grid(row=row, column=0,
                                                                                              columnspan=2,
                                                                                              sticky=tk.W + tk.E + tk.S)

        # 保存能力的按钮
        row += 1
        tk.Button(self.conf, text='Save capabilities', command=self.saveCapabilities).grid(row=row, column=0,
                                                                                           columnspan=2,
                                                                                           sticky=tk.W + tk.E + tk.S)
        self.conf.rowconfigure(row, weight=1)

    def displayModeInfos(self, modeID):
        '''
        在self.modeInfo标签中显示模式信息

        :param modeID: 预设模式标识符
        '''
        if not self.reader:
            return

        # 获取模式信息
        modes = self.reader.capabilities['RegulatoryCapabilities']['UHFBandCapabilities']['UHFRFModeTable']
        modeInfos = next(v for v in modes.values() if v['ModeIdentifier'] == modeID)

        # 转换为可理解的数据
        infos = ''

        # C: EPC HAG T&C一致性标志
        # 跳过，因为不感兴趣

        # M: 谱掩码指示器
        ms = {
            0: 'unknown',
            1: 'single reader',
            2: 'multi reader',
            3: 'dense reader'
        }
        m = ms[modeInfos['M']]
        infos += 'Spectral mask: {}\n'.format(m)

        # PIE: 数据1到数据0符号长度比例的1000倍（1.5...2.0）
        pie = modeInfos['PIE']
        infos += 'PIE ratio: {}\n'.format(pie / 1000)

        # Min/Step/Max Tari: Tari时间（数据0符号的持续时间）以ns为单位
        minTari, stepTari, maxTari = modeInfos['MinTari'], modeInfos['StepTari'], modeInfos['MaxTari']
        infos += 'Tari: '
        if maxTari != minTari:
            infos += '{}...{} ({} step) us\n'.format(minTari / 1000, maxTari / 1000, stepTari / 1000)
        else:
            infos += '{} us\n'.format(minTari / 1000)

        # R: DR值。与BDR一起定义TRcal（TRcal = DR/BDR）
        # 跳过，因为不感兴趣

        # BDR: 反向散射数据速率（bps），即标签频率除以调制因子
        blf = modeInfos['BDR']
        infos += 'Tag data rate: {} bps\n'.format(blf / 1000)

        # Mod: 调制/编码
        mods = {
            0: 'FM0',
            1: 'Miller2',
            2: 'Miller4',
            3: 'Miller8'
        }
        mod = mods[modeInfos['Mod']]
        infos += 'Tag modulation: {}'.format(mod)

        # 放入标签中
        self.modeInfo.set(infos)

    def connect(self):
        if not self.reader:
            try:
                self.readerClass = readerClasses[self.model.get()]
                self.reader = self.readerClass(self.ip.get())
            except:
                pass
            else:
                self.buildSettings()
                self.btnConnect.config(state=tk.DISABLED)

    def getSettings(self):
        # 从字符串中解析天线端口
        antStr = self.antennas.get()
        antennas = (0,) if 'all' in antStr else tuple(int(a) for a in antStr.split(','))

        # 使用设置进行盘存
        settings = {
            'powerDBm': self.power.get(),
            'freqMHz': self.freq.get(),
            'mode': self.presetmode.get(),
            'duration': self.duration.get(),
            'session': self.session.get(),
            'population': self.population.get(),
            'antennas': antennas
        }
        if isinstance(self.reader, R420):
            settings.update({'searchmode': self.searchmode.get()})

        return settings

    def singleInventory(self):
        if not self.reader:
            return

        settings = self.getSettings()
        self.tags = self.reader.detectTags(**settings)
        self.listTags()

    def liveInventory(self):
        if not self.reader:
            return

        if self.liveStateTxt.get() == 'Live inventory':
            # 开始实时盘存
            settings = self.getSettings()
            settings['timeInterval'] = settings.pop('duration')
            settings['tagInterval'] = None
            self.reader.startLiveReports(self.collectTags, **settings)
            self.liveStateTxt.set('Stop inventory')
        else:
            # 停止实时盘存
            self.reader._liveStop.set()
            self.liveStateTxt.set('Live inventory')

    def collectTags(self, tagreport):
        # 列出检测到的标签
        self.tags = tagreport
        self.listTags()

    def listTags(self):
        # 清空标签列表
        self.tagsHeader.set('')  # 清空摘要
        self.tagInfo.set('')  # 清空所选标签信息
        self.tagsDetected.delete(0, tk.END)  # 清空列表

        if not (self.reader and self.tags):
            return

        # 将找到的标签插入列表
        self.tagsHeader.set('{} Tags ({} unique)'.format(len(self.tags), len(self.reader.uniqueTags(self.tags))))
        for tag in self.tags:
            rssi = tag.get('RSSI', tag.get('PeakRSSI'))
            # 制作RSSI的指示器
            if rssi >= -40:
                stars = '*****'
            elif rssi >= -50:
                stars = '****'
            elif rssi >= -60:
                stars = '***'
            elif rssi >= -70:
                stars = '**'
            else:
                stars = '*'
            # 获取标签ID的最后3位数字
            epc = self.reader.getEPC(tag)
            id = int(epc[-2:], 16)
            # 插入包含EPC、ID和RSSI的行
            self.tagsDetected.insert(tk.END, '{} (...{}) | {}'.format(epc, id, stars))

    def trackTags(self):
        if self.reader:
            self.reader.includeEPCs = [self.reader.getEPC(tag) for tag in self.tags]

    def clearTrackedTags(self):
        if self.reader:
            self.reader.includeEPCs = []

    def selectTag(self, event):
        if self.tags:
            index = self.tagsDetected.curselection()[0]
            tag = self.tags[index]
            # 显示选定的标签信息
            infos = ''
            for key, val in tag.items():
                infos += '{}: {}\n'.format(key, val)
            self.tagInfo.set(infos)

    def saveCapabilities(self):
        filepath = tkFileDialog.asksaveasfilename(
            filetypes=[('JSON', '.json'), ('All files', '*')],
            defaultextension='.json',
            initialfile='capabilities.json'
        )
        if filepath and self.reader:  # 未取消
            with open(filepath, 'w') as file:
                file.write(json.dumps(self.reader.capabilities, indent=4))


if __name__ == '__main__':
    InventoryApp()
